//+--------------------------------------------------------------------------------
//| Copyright (c) 2012 - 2013 ### an.vuongngocduy
//| This file is a part of Compact Game Engine (CGE)
//+--------------------------------------------------------------------------------
#ifndef __CGE_VECTOR3_H__
#define __CGE_VECTOR3_H__

#include "cgeCommons.h"
#include "cgeMath.h"

namespace cge
{
	namespace std
	{
		/*! Vector3<T> template class . 
		 *	Used as a 3-dimensional direction vector or position of 3-dimensional point .
		 */
		template<class TVal>
		class Vector3
		{
		public:
			//! Default constructor
			Vector3()
				: X(0), Y(0), Z(0)
			{}


			//! Copy constructor
			Vector3(const Vector3& sz)
				: X(sz.X), Y(sz.Y), Z(sz.Z)
			{}


			//! Construct 3d-vector from specified coordinates
			Vector3(TVal x, TVal y, TVal z)
				: X(x), Y(y), Z(z)
			{}


			//! Return the length of vector
			f32 Length() const
			{
				return Math::Sqrt(X * X + Y * Y + Z * Z);
			}


			//! Set 3d-vector's values
			void Set(TVal x, TVal y, TVal z)
			{
				X = x;	Y = y;	Z = z;
			}


			//! Calculate and return the dot-product of 2 vectors
			f32 Dot(const Vector3& v3) const
			{
				return (X * v3.X + Y * v3.Y + Z * v3.Z);
			}


			//! Calculate and return the cross-product of 2 vectors
			Vector3 Cross(const Vector3& v3) const
			{
				return Vector3((Y * v3.Z) - (Z * v3.Y), 
					(Z * v3.X) - (X * v3.Z),
					(X * v3.Y) - (Y * v3.X));
			}


			//! Return angle in radian between 2 vectors
			f32 AngleBetween(const Vector3& v3) const
			{
				return Math::Acos(Dot(v3) / Math::Sqrt((X*X + Y*Y + Z*Z) * (v3.X*v3.X + v3.Y*v3.Y + v3.Z*v3.Z)));
			}


			//! Normalize current vector
			Vector3& Normalized()
			{
				f32 len = Length();
				X /= len;
				Y /= len;
				Z /= len;
				return *this;
			}

			Vector3 Normalize() const
			{
				f32 len = Length();
				return Vector3(X / len , Y / len, Z / len);
			}


			//! Multiply 3d-vector with scalar number
			template<class TArg>
			Vector3 operator * (TArg scalar) const
			{
				return Vector3(X * scalar, Y * scalar, Z * scalar);
			}


			//! Divide 3d-vector by scalar number
			template<class TArg>
			Vector3 operator / (TArg scalar) const
			{
				return Vector3(X / scalar, Y / scalar, Z / scalar);
			}


			//! Negate vector
			Vector3 operator - (void) const
			{
				return Vector3(-X, -Y, -Z);
			}


			//! Add 2 vectors together
			Vector3 operator + (const Vector3& v3) const
			{
				return Vector3(X + v3.X , Y + v3.Y, Z + v3.Z);
			}


			//! Subtract 2 vectors 
			Vector3 operator - (const Vector3& v3) const
			{
				return Vector3(X - v3.X , Y - v3.Y, Z - v3.Z);
			}
			

			//! Check whether 2 vectors are the same
			b8 operator == (const Vector3& v3) const
			{
				return (X == v3.X) && (Y == v3.Y) && (Z == v3.Z);
			}


			//! Check whether 2 vectors are different
			b8 operator != (const Vector3& v3) const
			{
				return (X != v3.X) || (Y != v3.Y) || (Z != v3.Z);
			}


			//! Multiply current vector with scalar number
			template<class TArg>
			Vector3& operator *= (TArg scalar)
			{
				X *= scalar;	Y *= scalar;	Z *= scalar; 
				return *this;
			}


			//! Divide current vector with scalar number
			template<class TArg>
			Vector3& operator /= (TArg scalar)
			{
				X /= scalar;	Y /= scalar;	Z /= scalar;
				return *this;
			}


			//! Return sum of current vector and another vector
			Vector3& operator += (const Vector3& v3)
			{
				X += v3.X;	Y += v3.Y;	Z += v3.Z;	
				return *this;
			}


			//! Subtract current vector with another vector
			Vector3& operator -= (const Vector3& v3)
			{
				X -= v3.X;	Y -= v3.Y;	Z -= v3.Z;
				return *this;
			}


			//! Assign operator . Copy specified vector's coordinate into current
			Vector3& operator = (const Vector3& v3)
			{
				Set(v3.X, v3.Y, v3.Z);
				return *this;
			}


		public:
			union
			{
				struct
				{
					TVal X, Y, Z;
				};
				TVal Coords[3];
			};
		};

		//! Signed integer 3d-vector
		typedef Vector3<s32> Vector3s;

		//! Unsigned integer 3d-vector
		typedef Vector3<u32> Vector3u;

		//! Floating number 3d-vector
		typedef Vector3<f32> Vector3f;

		//! Double precision floating number 3d-vector
		typedef Vector3<f64> Vector3d;
	}
}

#endif//__CGE_VECTOR3_H__